/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.jitag.ui.thumb;

import java.awt.*;
import javax.swing.JComponent;

/**
 * Abstract optical representation of file content, metadata and controls.
 */
public abstract class AbstractFileThumbnail extends JComponent {

    public static Color DEFAULT_thumbnailFrameColor = Color.WHITE;
    public static Color DEFAULT_thumbnailFrameBgColor = Color.WHITE;
    public static Color DEFAULT_fileFrameColor = new Color(0xE4E4E4);
    public static Color DEFAULT_fileFrameBgColor = Color.WHITE;
    public static Color DEFAULT_overallFrameColor = Color.WHITE;
    public static Color DEFAULT_overallFrameFocusedColor = new Color(0x294670);
    public static Color DEFAULT_overallFrameBgColor = Color.WHITE;
    public static Color DEFAULT_overallFrameSelectedBgColor = new Color(0x3169C6);

    public static int DEFAULT_overallFrameMargin = 4;
    public static int DEFAULT_fileFrameMargin = 3;

    //----------------------------------------------------------------------

    private Color thumbnailFrameColor = DEFAULT_thumbnailFrameColor;
    private Color thumbnailFrameBgColor = DEFAULT_thumbnailFrameBgColor;
    private Color fileFrameColor = DEFAULT_fileFrameColor;
    private Color fileFrameBgColor = DEFAULT_fileFrameBgColor;
    private Color overallFrameColor = DEFAULT_overallFrameColor;
    private Color overallFrameFocusedColor = DEFAULT_overallFrameFocusedColor;
    private Color overallFrameBgColor = DEFAULT_overallFrameBgColor;
    private Color overallFrameSelectedBgColor = DEFAULT_overallFrameSelectedBgColor;

    private Rectangle thumbnailFrame;
    private Rectangle fileFrame;
    private Rectangle overallFrame;
    private int thumbnailSize;
    private int controlsHeight;

    private boolean selected;
    private boolean focused;

    //----------------------------------------------------------------------

    /**
     * Initialize thumbnail.
     * @param thumbnailSize initial thumbnail maximal size (width = height).
     * @param controlsHeight height of controls pane under thumbnail image.
     */
    protected AbstractFileThumbnail(int thumbnailSize, int controlsHeight) {
        if(thumbnailSize < 0) throw new IllegalArgumentException("Parameter thumbnailSize can not be lesser than zero");
        if(controlsHeight < 0) throw new IllegalArgumentException("Parameter controlsHeight can not be lesser than zero");

        this.thumbnailSize = thumbnailSize;
        this.controlsHeight = controlsHeight + DEFAULT_fileFrameMargin;

        selected = false;
        focused = false;
        overallFrame = new Rectangle(0, 0,
             thumbnailSize + 2 * (DEFAULT_overallFrameMargin + DEFAULT_fileFrameMargin) + 2,
             thumbnailSize + 2 * (DEFAULT_overallFrameMargin + DEFAULT_fileFrameMargin) + 2
                + this.controlsHeight);
        fileFrame = new Rectangle(DEFAULT_overallFrameMargin, DEFAULT_overallFrameMargin,
                                  overallFrame.width - DEFAULT_overallFrameMargin*2,
                                  overallFrame.height - DEFAULT_overallFrameMargin*2);
        thumbnailFrame = new Rectangle(DEFAULT_overallFrameMargin + DEFAULT_fileFrameMargin,
                                       DEFAULT_overallFrameMargin + DEFAULT_fileFrameMargin,
                                       thumbnailSize+2, thumbnailSize+2);
    }

    //----------------------------------------------------------------------

    /**
     * Returns maximal thumbnail size (width = height).
     * @return maximal thumbnail size (width = height).
     */
    public int getThumbnailSize() {
        return thumbnailSize;
    }

    /**
     * Changes thubmnail maximal size.
     * @param thumbnailSize new size.
     */
    public synchronized void setThumbnailSize(int thumbnailSize) {
        this.thumbnailSize = thumbnailSize;
    }

    /**
     * Set selected state.
     * @param selected
     */
    public synchronized void setSelected(boolean selected) {
        this.selected = selected;
        repaint();
    }

    /**
     * Returns selected state.
     * @return selected state.
     */
    public boolean isSelected() {
        return selected;
    }

    /**
     * Set focused state.
     * @param focused
     */
    public synchronized void setFocused(boolean focused) {
        this.focused = focused;
        repaint();
    }

    /**
     * Returns focused state.
     * @return focused state.
     */
    public boolean isFocused() {
        return focused;
    }

    @Override
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"})
    @Override
    public synchronized Dimension getPreferredSize() {
        return new Dimension(overallFrame.width, overallFrame.height);
    }

    @Override
    public Dimension getMaximumSize() {
        return getPreferredSize();
    }

    //----------------------------------------------------------------------

    @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"})
    @Override
    protected synchronized void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();
        try {
            setOpaque(true);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            Color ofColor;
            Color ofBgColor;
            Stroke origStroke = g2d.getStroke();
            BasicStroke stroke;
            if(isFocused() && isSelected()){
                ofColor = overallFrameFocusedColor;
                ofBgColor = overallFrameSelectedBgColor;
                stroke = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER,
                                         10.0f, new float[]{2.0f}, 0.0f);
            }else if(isFocused()){
                ofColor = overallFrameFocusedColor;
                ofBgColor = overallFrameBgColor;
                stroke = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER,
                                         10.0f, new float[]{2.0f}, 0.0f);
            }else if(isSelected()){
                ofColor = overallFrameSelectedBgColor;
                ofBgColor = overallFrameSelectedBgColor;
                stroke = new BasicStroke();
            }else{
                ofColor = overallFrameColor;
                ofBgColor = overallFrameBgColor;
                stroke = new BasicStroke();
            }
            g2d.setColor(ofBgColor);
            g2d.drawRect(overallFrame.x, overallFrame.y, overallFrame.width-1, overallFrame.height-1);
            g2d.fillRect(overallFrame.x, overallFrame.y, overallFrame.width-1, overallFrame.height-1);
            g2d.setColor(ofColor);
            g2d.setStroke(stroke);
            g2d.drawRect(overallFrame.x, overallFrame.y, overallFrame.width-1, overallFrame.height-1);
            g2d.setStroke(origStroke);

            g2d.setColor(fileFrameBgColor);
            g2d.fillRect(fileFrame.x, fileFrame.y, fileFrame.width-1, fileFrame.height-1);
            g2d.setColor(fileFrameColor);
            g2d.drawRect(fileFrame.x, fileFrame.y, fileFrame.width-1, fileFrame.height-1);

            g2d.setColor(thumbnailFrameBgColor);
            g2d.fillRect(thumbnailFrame.x, thumbnailFrame.y, thumbnailFrame.width-1, thumbnailFrame.height-1);
            g2d.setColor(thumbnailFrameColor);
            g2d.drawRect(thumbnailFrame.x, thumbnailFrame.y, thumbnailFrame.width-1, thumbnailFrame.height-1);

            paintThumbnailContent(g2d);
        } finally {
            g2d.dispose();
        }

    }

    /**
     * Paints thumbnail image to thumbnail frame.
     * <p>For cout image location can be used methods
     * {@link #countThumbnailRealLocation(Dimension)}   
     * @param g2d
     */
    protected abstract void paintThumbnailContent(Graphics2D g2d);

    @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"})
    protected synchronized Point countThumbnailRealLocation(Dimension thumbnailRealSize){
        int thumbnailX = (getThumbnailSize() - thumbnailRealSize.width) / 2;
        int thumbnailY = (getThumbnailSize() - thumbnailRealSize.height) / 2;
        return new Point(thumbnailFrame.x + thumbnailX + 1,
                         thumbnailFrame.y + thumbnailY + 1);
    }

    /**
     * Helper method for count controls pane location and size in thumbnail
     * component.
     * @return controls pane location and size.
     */
    @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"})
    protected synchronized Rectangle countControlsBounds(){
        return new Rectangle(thumbnailFrame.x,
                             overallFrame.height - DEFAULT_overallFrameMargin - controlsHeight,
                             thumbnailFrame.width - 1,
                             controlsHeight - DEFAULT_fileFrameMargin - 1);
    }

}
